#ifndef LW_LINKED_LIST_H
#define LW_LINKED_LIST_H

#include <list>

#include "abstract/RandomAccessList.h"

template<typename T>
class LinkedList : public RandomAccessList<T> {
protected:
	std::list<T> wrappedList;
	static const std::string emptyAccessMessage;
	static const std::string indexOutOfBoundsMessage;

public:

	LinkedList() {

	}

	virtual ~LinkedList() {
	}

	/**
     * Compares specified list with this list and returns true if they are equal.
     * Two lists are equal if their size is the same, and each member in location i in one list is equal to the the member in location i in the other list.
     */
	virtual inline bool operator==(const LinkedList &v) const {
		return (wrappedList == v.wrappedList);
	}

	/**
     * Compares specified list with this list and returns true if they are not equal.
	 * Two lists are equal if their size is the same, and each member in location i in one list is equal to the the member in location i in the other list.
     */
	virtual inline bool operator!=(const LinkedList &v) const {
		return (wrappedList != v.wrappedList);
	}

	/**
     * Appends the specified element to the end of this list.
     */
	virtual inline void Add(const T &e) {
		RandomAccessList<T>::Add(e);
	}

	/**
     * Inserts the specified element at the specified position in this list.
     */
	virtual void Add(int index, const T &e) {
		if(index >= 0 && index <= Size()) {
			typename std::list<T>::iterator it = wrappedList.begin();
			for(int i=0; i<index; ++i) {
				++it;
			}
			wrappedList.insert(it, e);
			return;
		}

		throw std::out_of_range(indexOutOfBoundsMessage);
	}

	/**
     * Inserts the specified element at the beginning of this list.
     */
	virtual inline void AddFirst(const T &e) {
		wrappedList.push_front(e);
	}

	/**
     * Inserts the specified element at the end of this list.
     */
	virtual inline void AddLast(const T &e) {
		wrappedList.push_back(e);
	}

	/**
     * Removes all of the elements from this list.
     */
	virtual inline void Clear() {
		wrappedList.clear();
	}

	/**
     * Returns true if this list contains the specified element.
     */
	virtual bool Contains(const T &e) const {
		typename std::list<T>::const_iterator it = wrappedList.begin();
		while(it != wrappedList.end()) {
			if(*it == e) {
				return true;
			}
			++it;
		}
		return false;
	}

	/**
     * Sets or returns the element at the specified position in this list.
     */
	virtual T& operator[](int index) {
		if(index >= 0 && index < Size()) {
			typename std::list<T>::iterator it = wrappedList.begin();
			for(int i=0; i<index; ++i) {
				++it;
			}
			return *it;
		}

		throw std::out_of_range(indexOutOfBoundsMessage);
	}

	/**
     * Returns the element at the specified position in this list.
     */
	virtual const T& operator[](int index) const {
		if(index >= 0 && index < Size()) {
			typename std::list<T>::const_iterator it = wrappedList.begin();
			for(int i=0; i<index; ++i) {
				++it;
			}
			return *it;
		}

		throw std::out_of_range(indexOutOfBoundsMessage);
	}

	/**
     * Returns the first element in this list.
     */
	virtual inline const T& GetFirst() const {
		if(!Empty()) {
			return wrappedList.front();
		}

		throw std::runtime_error(emptyAccessMessage);
	}

	/**
     * Returns the last element in this list.
     */
	virtual inline const T& GetLast() const {
		if(!Empty()) {
			return wrappedList.back();
		}

		throw std::runtime_error(emptyAccessMessage);
	}

	/**
     * Returns the first element in this list.
     */
	virtual inline T& GetFirst() {
		if(!Empty()) {
			return wrappedList.front();
		}

		throw std::runtime_error(emptyAccessMessage);
	}

	/**
     * Returns the last element in this list.
     */
	virtual inline T& GetLast() {
		if(!Empty()) {
			return wrappedList.back();
		}

		throw std::runtime_error(emptyAccessMessage);
	}

	/**
     * Returns the index in this list of the first occurrence of the specified element, or -1 if the List does not contain this element.
     */
	virtual int IndexOf(const T& e) const {
		typename std::list<T>::const_iterator it = wrappedList.begin();
		int index = 0;
		while(it != wrappedList.end()) {
			if(*it == e) {
				return index;
			}
			++it;
			++index;
		}
		return -1;
	}

	/**
     * Returns the index in this list of the last occurrence of the specified element, or -1 if the list does not contain this element.
     */
	virtual int LastIndexOf(const T& e) const {
		typename std::list<T>::const_iterator it = wrappedList.end();
		for(int i = Size() - 1; i >=0; --i) {
			--it;
			if(*it == e) {
				return i;
			}
		}
		return -1;
	}

	/**
     * Returns an iterator to the first element of the list.
     */
	inline typename std::list<T>::iterator IteratorBegin() const {
		return wrappedList.begin();
	}

	/**
     * Returns an iterator to the end of the list (after the last element).
     */
	inline typename std::list<T>::iterator IteratorEnd() const {
		return wrappedList.end();
	}

	/**
     * Removes the element at the specified position in this list.
     */
	virtual inline void RemoveAt(int index) {
		if(index >= 0 && index < Size()) {
			typename std::list<T>::iterator it = wrappedList.begin();
			for(int i=0; i<index; ++i) {
				++it;
			}
			wrappedList.erase(it);
			return;
		}

		throw std::out_of_range(indexOutOfBoundsMessage);
	}

	/**
     * Removes all occurrences of v from this list.
     */
	virtual inline void Remove(const T &v) {
		wrappedList.remove(v);
	}

	/**
     * Removes the first element from this list.
     */
	virtual inline void RemoveFirst() {
		wrappedList.pop_front();
	}

	/**
     * Removes the last element from this list.
     */
	virtual inline void RemoveLast() {
		wrappedList.pop_back();
	}

	/**
     * Returns the number of elements in this list.
     */
	virtual inline int Size() const {
		return wrappedList.size();
	}

	/**
     * Reverses the order of the elements in this list.
     */
	virtual inline void Reverse() {
		wrappedList.reverse();
	}

	/**
     * Returns true if this list has no elements.
     */
	virtual inline bool Empty() const {
		return wrappedList.empty();
	}
};

template<typename T>
const std::string LinkedList<T>::indexOutOfBoundsMessage = "LinkedList (std::list) index out of range";

template<typename T>
const std::string LinkedList<T>::emptyAccessMessage = "LinkedList (std::list) is empty";

#endif //LW_LINKED_LIST_H
